﻿//(c) Alexandru Ghiondea, 2011
using System;
using System.Collections.Generic;
using JSONDotNET;
using JSONDotNET.Syntax;
using TestLibrary;

namespace JsonTests
{
    [TestGroup("Basic Parsing")]
    public class BasicParsing
    {
        [TestCase("Empty object")]
        public static void Test1()
        {
            var parse = JSONDotNET.Parser.ParseText("{ }");

            Helpers.Assert(parse != null, "We expected something to be parsed");
            Helpers.Assert(parse.Unit.Members.Count == 0, "Expected 0 members!!!");
        }

        [TestCase("Object with one property")]
        public static void Test2()
        {
            var parse = JSONDotNET.Parser.ParseText(@"{ ""test"": 123 }");

            Helpers.Assert(parse.Unit.Members[0].Name == "test");
            Helpers.Assert(parse.Unit.Members[0].Value.ToString() == "123", "Expected 123");
        }

        [TestCase("Object with one property starting with whitespace")]
        public static void Test3()
        {
            var parse = JSONDotNET.Parser.ParseText(@"    { ""test"": 123 }");

            Helpers.Assert(parse.Unit.Members[0].Name == "test");
            Helpers.Assert(parse.Unit.Members[0].Value.ToString() == "123", "Expected 123");
        }

        [TestCase("Object with one property with whitespace inside #1")]
        public static void Test4()
        {
            var parse = JSONDotNET.Parser.ParseText(@"{      ""test""      : 123 }         ");

            Helpers.Assert(parse.Unit.Members[0].Name == "test");
            Helpers.Assert(parse.Unit.Members[0].Value.ToString() == "123", "Expected 123");
        }

        [TestCase("Object with one property with whitespace inside #2")]
        public static void Test5()
        {
            var parse = JSONDotNET.Parser.ParseText(@"{    
        ""test""   
        :            123
        }         ");
        }

        [TestCase("Object with whitespace as escaped chars #1", TestcaseType.Negative)]
        public static void Test6()
        {
            try
            {
                var parse = JSONDotNET.Parser.ParseText(@"{    
        ""test""  \n
        :            123
        }         ");

            }
            catch (ParserException exc)
            {
                Helpers.Assert(exc.Position == 23);
            }
        }

        [TestCase("Object with whitespace as escaped chars #2", TestcaseType.Negative)]
        public static void Test7()
        {
            try
            {
                var parse = JSONDotNET.Parser.ParseText(@"{\n    
        ""test""
        :            123
        }         ");

            }
            catch (ParserException exc)
            {
                Helpers.Assert(exc.Position == 1);
            }
        }

        [TestCase("Whitespace as escaped chars", TestcaseType.Negative)]
        public static void Test8()
        {
            try
            {
                var parse = JSONDotNET.Parser.ParseText(@"\n \t \f ");
            }
            catch (ParserException exc)
            {
                Helpers.Assert(exc.Position == 0);
            }
        }

        [TestCase("Object with big number (integer)", TestcaseType.Negative)]
        public static void Test9()
        {
            try
            {
                var parse = JSONDotNET.Parser.ParseText(@"{ ""n"": 12345678901234567890 }");
            }
            catch (ParserException exc)
            {
                Helpers.Assert(exc.Position == 28);
                Helpers.Assert(exc.InnerException.GetType() == typeof(OverflowException));
            }
        }

        [TestCase("Object with big number (double) #1")]
        public static void Test9b()
        {
            var parse = JSONDotNET.Parser.ParseText(@"{ ""n"": 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.12345678901234567890 }");

            Helpers.Assert(parse.Unit.Members[0].Value.ToString() == double.PositiveInfinity.ToString());
        }

        [TestCase("Object with big number (double) #2")]
        public static void Test9c()
        {
            var parse = JSONDotNET.Parser.ParseText(@"{ ""n"": 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.12345678901234567890 }");

            Helpers.Assert(parse.Unit.Members[0].Value.ToString() == "1.23456789012346E+289");
        }

        [TestCase("Extra tokens after the end of the object", TestcaseType.Negative)]
        public static void Test10()
        {
            try
            {
                var parse = JSONDotNET.Parser.ParseText(@"{ ""test"" : 123  } : }");
            }
            catch (ParserException exc)
            {
                Helpers.Assert(exc.Position == 18);
                Helpers.Assert(exc.Message == "Object should not have trailing tokens! Found Colon after the CloseBrace");
            }
        }

        [TestCase("Parse string with invalid escaped characters")]
        public static void Test11()
        {
            var test = @"{ ""t"": ""\a \c"" }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert((parse.Unit.Members[0].Value as StringValue).Value == @"\a \c");
        }

        [TestCase("Parse string valid escaped characters")]
        public static void Test12()
        {
            var test = @"{ ""t"": ""\n \t \b \f \r \u1234"" }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert((parse.Unit.Members[0].Value as StringValue).Value == "\n \t \b \f \r ሴ");
        }
    }

    [TestGroup("Number parsing")]
    public class NumberParsing
    {
        [TestCase("Parse positive number")]
        public static void ParsePositiveNumber()
        {
            var test = @"{ ""t"": +1234 }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert((parse.Unit.Members[0].Value as IntNumberValue).Value == 1234);
        }

        [TestCase("Parse negative number")]
        public static void ParseNegativeNumber()
        {
            var test = @"{ ""t"": -1234 }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert((parse.Unit.Members[0].Value as IntNumberValue).Value == -1234);
        }

        [TestCase("Parse positive float number")]
        public static void ParsePositiveFloatNumber()
        {
            var test = @"{ ""t"": +1234.34 }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert((parse.Unit.Members[0].Value as DoubleNumberValue).Value == 1234.34);
        }

        [TestCase("Parse negative float number")]
        public static void ParseNegativeFloatNumber()
        {
            var test = @"{ ""t"": -1234.34 }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert((parse.Unit.Members[0].Value as DoubleNumberValue).Value == -1234.34);
        }

    }

    [TestGroup("Lexing")]
    public class Lexing
    {
        [TestCase("Lex Digits")]
        public static void LexInt()
        {
            for (int i = 0; i < 10; i++)
            {
                var test = i.ToString();
                var tokens = new Lexer(test).Lex();

                Helpers.Assert((tokens[0].kind == TokenKind.Digit) && tokens[0].value == test, "Cannot lex digit " + test);
            }
        }

        [TestCase("Lex positive number")]
        public static void LexPositiveNumber()
        {
            var test = @"+1234";
            var tokens = new Lexer(test).Lex();

            Helpers.Assert(tokens.Count == 5);
            Helpers.Assert(tokens[0].kind == TokenKind.Plus);

            test = @"+  1234";
            tokens = new Lexer(test).Lex();

            Helpers.Assert(tokens.Count == 5);
            Helpers.Assert(tokens[0].kind == TokenKind.Plus);

            test = @"      +  1234";
            tokens = new Lexer(test).Lex();

            Helpers.Assert(tokens.Count == 5);
            Helpers.Assert(tokens[0].kind == TokenKind.Plus);
        }

        [TestCase("Lex unicode")]
        public static void LexUnicode()
        {
            var test = @"\u1234";
            var tokens = new Lexer(test).Lex();

            Helpers.Assert(tokens[0].kind == TokenKind.EscapedCharacter);
            Helpers.Assert(tokens[0].value == "u");

            Helpers.Assert(tokens[1].value == "1");
            Helpers.Assert(tokens[2].value == "2");
            Helpers.Assert(tokens[3].value == "3");
            Helpers.Assert(tokens[4].value == "4");
        }

        [TestCase("Lex unicode inside a string")]
        public static void LexUnicode2()
        {
            var test = @"""\u1234""";
            var tokens = new Lexer(test).Lex();

            Helpers.Assert(tokens.Count == 3);
            Helpers.Assert(tokens[0].kind == TokenKind.DoubleQuote);
            Helpers.Assert(tokens[2].kind == TokenKind.DoubleQuote);

            Helpers.Assert(tokens[1].value == "\u1234");
        }

        [TestCase("Lex bad unicode inside a string", TestcaseType.Negative)]
        public static void LexUnicodeBad()
        {
            try
            {
                var test = @"""\uGGGG""";
                var tokens = new Lexer(test).Lex();
            }
            catch (LexerException e)
            {
                Helpers.Assert(e.Position == 2);
            }

            try
            {
                var test = @"""\u123G""";
                var tokens = new Lexer(test).Lex();
            }
            catch (LexerException e)
            {
                Helpers.Assert(e.Position == 2);
            }


            try
            {
                var test = @"""\uGAFD""";
                var tokens = new Lexer(test).Lex();
            }
            catch (LexerException e)
            {
                Helpers.Assert(e.Position == 2);
            }


            try
            {
                var test = @"""\u__23""";
                var tokens = new Lexer(test).Lex();
            }
            catch (LexerException e)
            {
                Helpers.Assert(e.Position == 2);
            }

            try
            {
                var test = @"""\u    """;
                var tokens = new Lexer(test).Lex();
            }
            catch (LexerException e)
            {
                Helpers.Assert(e.Position == 2);
            }
        }

        [TestCase("Lex escaped chars")]
        public static void LexEscapedChars()
        {
            var test = @"\n \f \r \t \b \u \N \F \R \T \B \U";
            var tokens = new Lexer(test).Lex();

            Helpers.Assert(tokens.Count == 12);

            string[] expected = new string[] { "n", "f", "r", "t", "b", "u", "N", "F", "R", "T", "B", "U" };
            for (int i = 0; i < tokens.Count; i++)
            {
                Helpers.Assert(tokens[i].kind == TokenKind.EscapedCharacter);
                Helpers.Assert(tokens[i].value == expected[i]);
            }
        }

        [TestCase("Lex invalid escaped chars", TestcaseType.Negative)]
        public static void LexInvalidEscapedChars()
        {
            var test = @"\c";
            try
            {
                var tokens = new Lexer(test).Lex();
            }
            catch (LexerException ex)
            {
                Helpers.Assert(ex.Position == 1);
            }
        }

        [TestCase("Lex escaped chars inside a string which are not special")]
        public static void LexEscapedCharsInsideString()
        {
            var test = @"""\a \c""";
            var tokens = new Lexer(test).Lex();

            Helpers.Assert(tokens.Count == 7);
            int expected = 0;
            foreach (var tok in tokens)
            {
                if (tok.value == "a" && tok.kind == TokenKind.Character)
                    expected++;
                if (tok.value == "c" && tok.kind == TokenKind.Character)
                    expected++;
            }
            Helpers.Assert(expected == 2, "Expected two characters");
        }

    }

    [TestGroup("Object Deserializing")]
    public class SimpleDeserializing
    {
        #region Helper objects
        enum E
        {
            A,
            B
        }
        class SimpleObj1
        {
            public int i { get; set; }
            public decimal l { get; set; }
            public string s { get; set; }
            public bool b { get; set; }
            public double d { get; set; }
            public float f { get; set; }

            public E e { get; set; }
        }
        class SimpleObj2
        {
            public int i;
            public string s;
            public bool b;
            public double d;
            public float f;

            public E e;
        }
        class NullableObj
        {
            public int? i { get; set; }
            public bool? b { get; set; }
            public double? d { get; set; }
            public float? f { get; set; }

            public E? e { get; set; }
        }
        class GenericObj1<T, U>
        {
            public T i { get; set; }
            public U b;
        }
        class GenericObj2<T, U>
            where T : struct
            where U : struct
        {
            public T? i { get; set; }
            public U? b;
        }
        #endregion

        [TestCase("Basic deserializing (as props)")]
        public static void Test1()
        {
            var test = "{ \"i\" : 1, \"l\" : 5, \"s\":\"aa\", \"b\":true, \"d\":1.3, \"f\":1.4, \"e\" : \"A\"}";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert(parse != null, "We expected something to be parsed");
            //Helpers.Assert(parse.Unit.Members.Count == 0, "Expected 0 members!!!");


            var obj = JSONDotNET.Deserializer.Deserialize<SimpleObj1>(parse);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.i == 1, "Expected i = 1");
            Helpers.Assert(obj.l == 5, "Expected l = 5");
            Helpers.Assert(obj.s == "aa", "Expected s = aa");
            Helpers.Assert(obj.b == true, "Expected b = true");
            Helpers.Assert(obj.d == 1.3, "Expected d = 1.3");
            Helpers.Assert(obj.f == 1.4f, "Expected f = 1.4");
            Helpers.Assert(obj.e == E.A, "Expected e = A");
        }

        [TestCase("Basic deserializing (as fields)")]
        public static void Test1b()
        {
            var test = "{ \"i\" : 1, \"s\":\"aa\", \"b\":true, \"d\":1.3, \"f\":1.4, \"e\" : \"A\"}";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert(parse != null, "We expected something to be parsed");
            //Helpers.Assert(parse.Unit.Members.Count == 0, "Expected 0 members!!!");


            var obj = JSONDotNET.Deserializer.Deserialize<SimpleObj2>(parse);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.i == 1, "Expected i = 1");
            Helpers.Assert(obj.s == "aa", "Expected s = aa");
            Helpers.Assert(obj.b == true, "Expected b = true");
            Helpers.Assert(obj.d == 1.3, "Expected d = 1.3");
            Helpers.Assert(obj.f == 1.4f, "Expected f = 1.4");
            Helpers.Assert(obj.e == E.A, "Expected e = A");
        }

        [TestCase("Deserializing enum as integer")]
        public static void Test2()
        {
            var test = "{ \"e\" : 0 }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert(parse != null, "We expected something to be parsed");
            //Helpers.Assert(parse.Unit.Members.Count == 0, "Expected 0 members!!!");


            var obj = JSONDotNET.Deserializer.Deserialize<SimpleObj1>(parse);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.e == E.A, "Expected e = A");
        }

        [TestCase("Deserializing into nullable")]
        public static void Test3()
        {
            var test = "{ \"i\" : 1, \"b\":true, \"d\":1.3, \"f\":1.4, \"e\" : \"A\"}";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert(parse != null, "We expected something to be parsed");
            //Helpers.Assert(parse.Unit.Members.Count == 0, "Expected 0 members!!!");


            var obj = JSONDotNET.Deserializer.Deserialize<NullableObj>(parse);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.i == 1, "Expected i = 1");
            Helpers.Assert(obj.b == true, "Expected b = true");
            Helpers.Assert(obj.d == 1.3, "Expected d = 1.3");
            Helpers.Assert(obj.f == 1.4f, "Expected f = 1.4");
            Helpers.Assert(obj.e == E.A, "Expected e = A");
        }

        [TestCase("Deserializing into nullable (explicit null)")]
        public static void Test3b()
        {
            var test = "{ \"i\" : null, \"b\": null , \"d\": null, \"f\":null, \"e\" : null}";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert(parse != null, "We expected something to be parsed");
            //Helpers.Assert(parse.Unit.Members.Count == 0, "Expected 0 members!!!");


            var obj = JSONDotNET.Deserializer.Deserialize<NullableObj>(parse);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.i == null, "Expected i = null");
            Helpers.Assert(obj.b == null, "Expected b = null");
            Helpers.Assert(obj.d == null, "Expected d = null");
            Helpers.Assert(obj.f == null, "Expected f = null");
            Helpers.Assert(obj.e == null, "Expected e = null");
        }

        [TestCase("Deserializing into a generic field and prop")]
        public static void Test4()
        {
            var test = "{ \"i\" : 1, \"b\" : false }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert(parse != null, "We expected something to be parsed");
            //Helpers.Assert(parse.Unit.Members.Count == 0, "Expected 0 members!!!");


            var obj = JSONDotNET.Deserializer.Deserialize<GenericObj1<int, bool>>(parse);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.i == 1, "Expected i = 1");
            Helpers.Assert(obj.b == false, "Expected b = false");
        }

        [TestCase("Deserializing into a nullable generic field and prop")]
        public static void Test4b()
        {
            var test = "{ \"i\" : 1, \"b\" : false }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert(parse != null, "We expected something to be parsed");
            //Helpers.Assert(parse.Unit.Members.Count == 0, "Expected 0 members!!!");


            var obj = JSONDotNET.Deserializer.Deserialize<GenericObj1<int, bool>>(parse);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.i == 1, "Expected i = 1");
            Helpers.Assert(obj.b == false, "Expected b = false");
        }

        [TestCase("Deserializing into a nullable generic field and prop (explicit null)")]
        public static void Test4c()
        {
            var test = "{ \"i\" : null, \"b\" : null }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert(parse != null, "We expected something to be parsed");
            //Helpers.Assert(parse.Unit.Members.Count == 0, "Expected 0 members!!!");


            var obj = JSONDotNET.Deserializer.Deserialize<GenericObj2<int, bool>>(parse);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.i == null, "Expected i = null");
            Helpers.Assert(obj.b == null, "Expected b = null");
        }
    }

    [TestGroup("Array Deserializing")]
    public class ArrayDeserializing
    {
        #region Helper objects
        enum E
        {
            A,
            B
        }
        class SimpleObj1
        {
            public int[] i { get; set; }
            public string[] s { get; set; }
            public bool[] b { get; set; }
            public double[] d { get; set; }
            public float[] f { get; set; }

            public E[] e { get; set; }
        }
        class SimpleObj2
        {
            public int[] i;
            public string[] s;
            public bool[] b;
            public double[] d;
            public float[] f;

            public E[] e;
        }
        class SimpleObj3
        {
            public List<int> i { get; set; }
            public List<string> s { get; set; }
            public List<bool> b;
            public List<double> d;
            public List<float> f { get; set; }

            public List<E> e { get; set; }
        }

        class RefTypeObj
        {
            public NestedType[] ar;
        }
        class GenericRefTypeObj<T> where T : NestedType
        {
            public T[] ar;
            public T obj;
        }
        class NestedType
        {
            public int Prop { get; set; }
        }
        #endregion

        [TestCase("Basic array deserializing (as props)")]
        public static void Test1()
        {
            var test = "{ \"i\" : [1,2,3], \"s\": [\"aa\", \"bb\"], \"b\": [true, false], \"d\": [1.3, 1.4],  \"f\": [1.4,-0.4], \"e\" : [\"A\", 1] }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert(parse != null, "We expected something to be parsed");

            var obj = JSONDotNET.Deserializer.Deserialize<SimpleObj1>(parse);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.i.Length == 3, "Expected obj.i.Length = 3");
            Helpers.Assert(obj.i[0] == 1);
            Helpers.Assert(obj.i[1] == 2);
            Helpers.Assert(obj.i[2] == 3);

            Helpers.Assert(obj.s.Length == 2, "Expected obj.s.Length = 2");
            Helpers.Assert(obj.s[0] == "aa");
            Helpers.Assert(obj.s[1] == "bb");

            Helpers.Assert(obj.b.Length == 2, "Expected obj.b.Length = 2");
            Helpers.Assert(obj.b[0] == true);
            Helpers.Assert(obj.b[1] == false);

            Helpers.Assert(obj.d.Length == 2, "Expected obj.d.Length = 2");
            Helpers.Assert(obj.d[0] == 1.3);
            Helpers.Assert(obj.d[1] == 1.4);

            Helpers.Assert(obj.f.Length == 2, "Expected obj.f.Length = 2");
            Helpers.Assert(obj.f[0] == 1.4f);
            Helpers.Assert(obj.f[1] == -0.4f);

            Helpers.Assert(obj.e.Length == 2, "Expected obj.e.Length = 2");
            Helpers.Assert(obj.e[0] == E.A);
            Helpers.Assert(obj.e[1] == E.B);
        }

        [TestCase("Basic array deserializing (as fields)")]
        public static void Test1b()
        {
            var test = "{ \"i\" : [1,2,3], \"s\": [\"aa\", \"bb\"], \"b\": [true, false], \"d\": [1.3, 1.4],  \"f\": [1.4,-0.4], \"e\" : [\"A\", 1] }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert(parse != null, "We expected something to be parsed");

            var obj = JSONDotNET.Deserializer.Deserialize<SimpleObj2>(parse);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.i.Length == 3, "Expected obj.i.Length = 3");
            Helpers.Assert(obj.i[0] == 1);
            Helpers.Assert(obj.i[1] == 2);
            Helpers.Assert(obj.i[2] == 3);

            Helpers.Assert(obj.s.Length == 2, "Expected obj.s.Length = 2");
            Helpers.Assert(obj.s[0] == "aa");
            Helpers.Assert(obj.s[1] == "bb");

            Helpers.Assert(obj.b.Length == 2, "Expected obj.b.Length = 2");
            Helpers.Assert(obj.b[0] == true);
            Helpers.Assert(obj.b[1] == false);

            Helpers.Assert(obj.d.Length == 2, "Expected obj.d.Length = 2");
            Helpers.Assert(obj.d[0] == 1.3);
            Helpers.Assert(obj.d[1] == 1.4);

            Helpers.Assert(obj.f.Length == 2, "Expected obj.f.Length = 2");
            Helpers.Assert(obj.f[0] == 1.4f);
            Helpers.Assert(obj.f[1] == -0.4f);

            Helpers.Assert(obj.e.Length == 2, "Expected obj.e.Length = 2");
            Helpers.Assert(obj.e[0] == E.A);
            Helpers.Assert(obj.e[1] == E.B);
        }

        [TestCase("Basic list deserializing")]
        public static void Test4()
        {
            var test = "{ \"i\" : [1,2,3], \"s\": [\"aa\", \"bb\"], \"b\": [true, false], \"d\": [1.3, 1.4],  \"f\": [1.4,-0.4], \"e\" : [\"A\", 1] }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert(parse != null, "We expected something to be parsed");

            var obj = JSONDotNET.Deserializer.Deserialize<SimpleObj1>(parse);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.i.Length == 3, "Expected obj.i.Length = 3");
            Helpers.Assert(obj.i[0] == 1);
            Helpers.Assert(obj.i[1] == 2);
            Helpers.Assert(obj.i[2] == 3);

            Helpers.Assert(obj.s.Length == 2, "Expected obj.s.Length = 2");
            Helpers.Assert(obj.s[0] == "aa");
            Helpers.Assert(obj.s[1] == "bb");

            Helpers.Assert(obj.b.Length == 2, "Expected obj.b.Length = 2");
            Helpers.Assert(obj.b[0] == true);
            Helpers.Assert(obj.b[1] == false);

            Helpers.Assert(obj.d.Length == 2, "Expected obj.d.Length = 2");
            Helpers.Assert(obj.d[0] == 1.3);
            Helpers.Assert(obj.d[1] == 1.4);

            Helpers.Assert(obj.f.Length == 2, "Expected obj.f.Length = 2");
            Helpers.Assert(obj.f[0] == 1.4f);
            Helpers.Assert(obj.f[1] == -0.4f);

            Helpers.Assert(obj.e.Length == 2, "Expected obj.e.Length = 2");
            Helpers.Assert(obj.e[0] == E.A);
            Helpers.Assert(obj.e[1] == E.B);
        }

        [TestCase("Basic ref-type array deserializing")]
        public static void Test2()
        {
            var test = "{ \"ar\" : [ { \"Prop\":1 }, { \"Prop\":2 } ] }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert(parse != null, "We expected something to be parsed");

            var obj = JSONDotNET.Deserializer.Deserialize<RefTypeObj>(parse);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.ar.Length == 2, "Expected ar.Length = 2");
            Helpers.Assert(obj.ar[0].Prop == 1);
            Helpers.Assert(obj.ar[1].Prop == 2);
        }

        [TestCase("Ref-type array deserializing")]
        public static void Test3()
        {
            var test = @"{ 
                ""ar"" :  [  
                            { ""Prop"":1 }, 
                            { ""Prop"":2 } 
                           ] ,
                ""obj"" :  { 
                            ""Prop"":3 
                           }
                          }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert(parse != null, "We expected something to be parsed");

            var obj = JSONDotNET.Deserializer.Deserialize<GenericRefTypeObj<NestedType>>(parse);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.ar.Length == 2, "Expected ar.Length = 2");
            Helpers.Assert(obj.ar[0].Prop == 1);
            Helpers.Assert(obj.ar[1].Prop == 2);
            Helpers.Assert(obj.obj.Prop == 3);
        }
    }

    [TestGroup("Type conversions")]
    public class TypeConversions
    {
        class SimpleObj1
        {
            public byte b;
            public sbyte sb;
            public short s;
            public ushort us;
            public int i;
            public uint ui;
            public long l;
            public ulong ul;

            public float f;
            public double d;

            public decimal dc;
        }

        [TestCase("Type conversions")]
        public static void Test1()
        {
            var test = "{ \"b\" : 1, \"sb\":2, \"s\":3, \"us\":4, \"i\":5, \"ui\":6, \"l\":7, \"ul\": 8, \"f\":9, \"d\":10, \"dc\":11 }";
            var parse = JSONDotNET.Parser.ParseText(test);

            Helpers.Assert(parse != null, "We expected something to be parsed");

            var obj = JSONDotNET.Deserializer.Deserialize<SimpleObj1>(parse);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.b == 1);
            Helpers.Assert(obj.sb == 2);
            Helpers.Assert(obj.s == 3);
            Helpers.Assert(obj.us == 4);
            Helpers.Assert(obj.i == 5);
            Helpers.Assert(obj.ui == 6);
            Helpers.Assert(obj.l == 7);
            Helpers.Assert(obj.ul == 8);
            Helpers.Assert(obj.f == 9);
            Helpers.Assert(obj.d == 10);
            Helpers.Assert(obj.dc == 11);
        }

        [TestCase("Value too big / small (byte)", TestcaseType.Negative)]
        public static void Test2()
        {
            try
            {
                var test = "{ \"b\" : 260 }";
                var parse = JSONDotNET.Parser.ParseText(test);
                var obj = JSONDotNET.Deserializer.Deserialize<SimpleObj1>(parse);
            }
            catch (OverflowException)
            {
            }

            try
            {
                var test = "{ \"b\" : -1 }";
                var parse = JSONDotNET.Parser.ParseText(test);
                var obj = JSONDotNET.Deserializer.Deserialize<SimpleObj1>(parse);
            }
            catch (OverflowException)
            {
            }
        }

        [TestCase("Value too big / small (int)", TestcaseType.Negative)]
        public static void Test3()
        {
            try
            {
                var test = "{ \"i\" :" + unchecked(int.MaxValue + 1).ToString() + " }";
                var parse = JSONDotNET.Parser.ParseText(test);
                var obj = JSONDotNET.Deserializer.Deserialize<SimpleObj1>(parse);
            }
            catch (OverflowException)
            {
            }

            try
            {
                var test = "{ \"i\" :" + unchecked(int.MinValue - 1).ToString() + " }";
                var parse = JSONDotNET.Parser.ParseText(test);
                var obj = JSONDotNET.Deserializer.Deserialize<SimpleObj1>(parse);
            }
            catch (OverflowException)
            {
            }
        }
        [TestCase("Value too big / small (long)", TestcaseType.Negative)]
        public static void Test4()
        {
            try
            {
                var test = "{ \"l\" :" + unchecked(long.MaxValue + 1).ToString() + " }";
                var parse = JSONDotNET.Parser.ParseText(test);
                var obj = JSONDotNET.Deserializer.Deserialize<SimpleObj1>(parse);
            }
            catch (ParserException ex)
            {
                Helpers.Assert(ex.InnerException.GetType() == typeof(OverflowException));
            }

            try
            {
                var test = "{ \"l\" :" + unchecked(long.MinValue - 1).ToString() + " }";
                var parse = JSONDotNET.Parser.ParseText(test);
                var obj = JSONDotNET.Deserializer.Deserialize<SimpleObj1>(parse);
            }
            catch (ParserException ex)
            {
                Helpers.Assert(ex.InnerException.GetType() == typeof(OverflowException));
            }
        }

        [TestCase("Value too big / small (double)", TestcaseType.Negative)]
        public static void Test5()
        {
            try
            {
                var test = "{ \"d\" :" + unchecked(double.MaxValue + 1).ToString() + " }";
                var parse = JSONDotNET.Parser.ParseText(test);
                var obj = JSONDotNET.Deserializer.Deserialize<SimpleObj1>(parse);
            }
            catch (ParserException ex)
            {
                Helpers.Assert(ex.InnerException.GetType() == typeof(OverflowException));
            }

            try
            {
                var test = "{ \"d\" :" + unchecked(double.MinValue - 1).ToString() + " }";
                var parse = JSONDotNET.Parser.ParseText(test);
                var obj = JSONDotNET.Deserializer.Deserialize<SimpleObj1>(parse);
            }
            catch (ParserException ex)
            {
                Helpers.Assert(ex.InnerException.GetType() == typeof(OverflowException));
            }
        }
    }

    [TestGroup("SmugMug real world objects")]
    public class SmugMugResponses
    {
        [TestCase("SmugMug.Albums.get")]
        public static void ParseAlbumsGet()
        {
            var test = @"{
  ""stat"": ""ok"",
  ""method"": ""smugmug.albums.get"",
  ""Albums"": [
    {
      ""id"": 1234,
      ""Key"": ""xCXXu"",
      ""Category"": {
        ""id"": 0,
        ""Name"": ""Other""
      },
      ""Title"": ""My Birthday 2008""
    }
  ]
}";
            var parse = JSONDotNET.Parser.ParseText(test);
        }

        [TestCase("SmugMug.User.GetTree")]
        public static void ParseUserGetTree()
        {
            var test = @"{
  ""stat"": ""ok"",
  ""method"": ""smugmug.users.getTree"",
  ""Categories"": [
    {
      ""id"": 0,
      ""Name"": ""Other"",
      ""SubCategories"": [
        {
          ""id"": 1000,
          ""Name"": ""Testing""
        }
      ],
      ""Albums"": [
        {
          ""id"": 1234,
          ""Key"": ""xCXXu"",
          ""Title"": ""My Birthday 2008""
        }
      ]
    },
    {
      ""id"": 1,
      ""Name"": ""Animals""
    },
    {
      ""id"": 2,
      ""Name"": ""Architecture""
    },
    {
      ""id"": 3,
      ""Name"": ""Art""
    },
    {
      ""id"": 4,
      ""Name"": ""Boats""
    },
    {
      ""id"": 13,
      ""Name"": ""Landscapes"",
      ""SubCategories"": [
        {
          ""id"": 1234,
          ""Name"": ""Sunrise""
        },
        {
          ""id"": 2345,
          ""Name"": ""Sunset""
        }
      ]
    }
  ]
}";
            var parse = JSONDotNET.Parser.ParseText(test);
        }
    }

    [TestGroup("Deserializing using TypeHints on object properties")]
    public class DeserializingWithTestHintAttributes
    {
        #region Helper objects
        class PropsFields
        {
            [TypeHint(typeof(bool))]
            public object Prop { get; set; }

            [TypeHint(typeof(double))]
            public object Field = null;
        }

        class Lists
        {
            [TypeHint(typeof(List<int>))]
            public object Prop { get; set; }

            [TypeHint(typeof(List<double>))]
            public object Field = null;
        }

        class Arrays
        {
            [TypeHint(typeof(uint[]))]
            public object Prop { get; set; }

            [TypeHint(typeof(double[]))]
            public object Field = null;
        }

        class ComplexArraysLists
        {
            [TypeHint(typeof(MyClass))]
            public object Complex = null;
        }

        class MyClass
        {
            public string Name { get; set; }

            [TypeHint(typeof(string))]
            public object Value { get; set; }

            [TypeHint(typeof(int[]))]
            public object List { get; set; }
        }

        class ArraysOfComplexTypes
        {
            [TypeHint(typeof(List<MyClass>))]
            [TypeHint(typeof(MyClass[]))]
            public object Values;
        }
        #endregion

        [TestCase("TestHintAttribute on simple types")]
        public static void SimpleTypes()
        {
            var test = "{ \"Prop\" : true, \"Field\" : 3.3 }";
            var obj = JSONDotNET.Deserializer.Deserialize<PropsFields>(test);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.Prop.GetType() == typeof(bool));
            Helpers.Assert((bool)obj.Prop == true);
            Helpers.Assert(obj.Field.GetType() == typeof(double));
            Helpers.Assert((double)obj.Field == 3.3);
        }

        [TestCase("TestHintAttribute on lists of simple types")]
        public static void ListSimpleTypes()
        {
            var test = "{ \"Prop\" : [ 1 , 2], \"Field\" : [1.2, 3.3] }";
            var obj = JSONDotNET.Deserializer.Deserialize<Lists>(test);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.Prop.GetType() == typeof(List<int>));
            Helpers.Assert((obj.Prop as List<int>)[0] == 1);
            Helpers.Assert(obj.Field.GetType() == typeof(List<double>));
            Helpers.Assert((obj.Field as List<double>)[0] == 1.2);
        }

        [TestCase("TestHintAttribute on arrays of simple types")]
        public static void ArraysSimpleTypes()
        {
            var test = "{ \"Prop\" : [ 1 , 2], \"Field\" : [1.2, 3.3] }";
            var obj = JSONDotNET.Deserializer.Deserialize<Arrays>(test);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.Prop.GetType() == typeof(uint[]));
            Helpers.Assert(((uint[])obj.Prop)[0] == 1);
            Helpers.Assert(obj.Field.GetType() == typeof(double[]));
            Helpers.Assert(((double[])obj.Field)[0] == 1.2);
        }

        [TestCase("TestHintAttribute on nested types")]
        public static void NestedTypes()
        {
            var test = "{ \"Complex\" : { \"Name\" : \"Test\", \"Value\" : \"bar\", \"List\" : [1, 2, 3] } }";
            var obj = JSONDotNET.Deserializer.Deserialize<ComplexArraysLists>(test);
            Helpers.Assert(obj != null, "Expected the object to be not null");

            Helpers.Assert(obj.Complex.GetType() == typeof(MyClass));

            MyClass c = obj.Complex as MyClass;
            Helpers.Assert(c != null);

            Helpers.Assert(c.Name == "Test");
            Helpers.Assert(c.Value.GetType() == typeof(string));
            Helpers.Assert(c.Value as string == "bar");

            Helpers.Assert(c.List.GetType() == typeof(int[]));
        }

        [TestCase("TestHintAttribute on complex nested types")]
        public static void NestedComplexTypes()
        {
            var test = "{ \"Values\" : [                  { \"Name\" : \"Test\", \"Value\" : \"bar\", \"List\" : [1, 2, 3] } ,                   { \"Name\" : \"Test2\", \"Value\" : \"bar2\", \"List\" : [4, 5, 6] } ] } ";
            var obj = JSONDotNET.Deserializer.Deserialize<ArraysOfComplexTypes>(test);
            Helpers.Assert(obj != null, "Expected the object to be not null");


            Helpers.Assert(obj.Values.GetType() == typeof(List<MyClass>), "A list is better than an array");

            var v = obj.Values as List<MyClass>;
            Helpers.Assert(v.Count == 2);

            Helpers.Assert(v[0].List.GetType() == typeof(int[]));
        }
    }
}
